using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Linq;
using Mpi.Common;
using Mpi.Common.Collections;
using Xeml.Document.Contracts;
using Xeml.Document.Manager;
using Xeml.Document.Ontologies;
using System.Text.RegularExpressions;
using Xeml.Document.EnvironmentalOntology;

namespace Xeml.Document.Ontologies.OBO
{
    public class XeoHandler : OboHandler, IDynamicOntologyHandler
    {
        public static readonly string URI = "http://xeml.mpimp-golm.mpg.de/xemlobo";

        #region constructors
        public XeoHandler()
            :base()
        {
            //InstanceLocation = @"http://gmd.mpimp-golm.mpg.de/XEML/XeO_v1_6.obo";
            InstanceLocation = @"http://rest.bioontology.org/bioportal/virtual/download/3176?apikey=db0a05bc-af0e-4e24-99e6-02f2cb830b7d";
        }

        public XeoHandler(XeoHandler other)
            : base()
        {
            InstanceLocation = other.InstanceLocation;
        }
        #endregion constructors
    
        #region IXemlComponent Members
        public string Publisher
        {
            get { return "Max Planck Institute of Molecular Plant Physiology, Potsdam/Golm, Germany"; }
        }

        private Version _version = new Version(1, 0, 0, 0);
        public Version Version
        {
            get { return _version; }
        }

        public new string ComponentName
        {
            get { return "XEML Environment Ontolgy Handler"; }
        }

        public string Author
        {
            get { return "Jan Hummel"; }
        }

        public string Description
        {
            get { return "The XEML framework standard component for the definiton of environments. This component handles the environment ontolgy in OBO file format."; }
        }

        public string Uri
        {
            get { return URI; }
        }

        public IXemlComponent Copy()
        {
            return new XeoHandler(this);
        }
        #endregion

        protected override void DoTagTypeAnnotation(ParsedOboFile collection)
        {
            List<string> _environmentTerms = new List<string>();
            List<string> _contextTerms = new List<string>();
            List<string> _datatypeTerms = new List<string>();

            bool altered = true;
            while (altered)
            {
                altered = false;
                foreach (var item in collection.AsParallel().AsUnordered().Where(x => x.StanzaType == OboStanzaTypes.Term && x.isNoneTerm))
                {
                    if (item.Any(x => x.Item1 == OboTags.is_a))
                    {
                        int index = _environmentTerms.BinarySearch(item.Single(x => x.Item1 == OboTags.is_a).Item2);
                        if (index >= 0)
                        {
                            index = _environmentTerms.BinarySearch(item.Id);
                            if (index < 0)
                            {
                                item.HeaderClass = OboTermClass.EnvironmentVariable;
                                _environmentTerms.Insert(~index, item.Id);
                            }
                            else
                            {
                                throw new Exception(string.Concat("Duplicated environment identifier '", item.Id, "' found'"));
                            }
                            altered = true;
                            continue;
                        }

                        index = _contextTerms.BinarySearch(item.Single(x => x.Item1 == OboTags.is_a).Item2);
                        if (index >= 0)
                        {
                            index = _contextTerms.BinarySearch(item.Id);
                            if (index < 0)
                            {
                                item.HeaderClass = OboTermClass.Context;
                                _contextTerms.Insert(~index, item.Id);
                            }
                            else
                            {
                                throw new Exception(string.Concat("Duplicated context identifier '", item.Id, "' found'"));
                            }
                            altered = true;
                            continue;
                        }

                        index = _datatypeTerms.BinarySearch(item.Single(x => x.Item1 == OboTags.is_a).Item2);
                        if (index >= 0)
                        {
                            index = _datatypeTerms.BinarySearch(item.Id);
                            if (index < 0)
                            {
                                item.HeaderClass = OboTermClass.DataType;
                                _datatypeTerms.Insert(~index, item.Id);
                            }
                            else
                            {
                                throw new Exception(string.Concat("Duplicated datatype identifier '", item.Id, "' found'"));
                            }
                            altered = true;
                            continue;
                        }
                    }
                    else
                    {
                        if (!altered)
                        {
                            switch (item.Name)
                            {
                                case "EnvironmentVariable":
                                    _environmentTerms.Insert(0, item.Id);
                                    item.HeaderClass = OboTermClass.EnvironmentVariable;
                                    break;
                                case "Context":
                                    _contextTerms.Insert(0, item.Id);
                                    item.HeaderClass = OboTermClass.Context;
                                    break;
                                case "DataTypes":
                                    _datatypeTerms.Insert(0, item.Id);
                                    item.HeaderClass = OboTermClass.DataType;
                                    break;
                                default:
                                    throw new Exception("expected root element from set 'EnvironmentVariable', 'Context' or 'DataTypes'");
                            }
                            altered = true;
                            continue;
                        }
                    }
                }
            }
        }

        protected override void BuildContextes(ParsedOboFile collection)
        {
            //this dictionary maps from the ContextId to the actual context object
            SortedDictionary<string, VariableContextSpec> contextCollection = new SortedDictionary<string, VariableContextSpec>();

            /*
             * first run: create all contexts for Environmentsvariables with "has_context" relations
             * iterate the attributelist for all used contexte
             */

            int i = collection.Count(x => x.isEnvironmentVariableTerm && x.Any(y => y.Item1 == OboTags.has_context));

            foreach (OboStanza currentXeoTermWithContext in collection.Where(x => x.isEnvironmentVariableTerm && x.Any(y => y.Item1 == OboTags.has_context)))
            {
                foreach (Tuple<OboTags, string> attr in currentXeoTermWithContext.Where(x => x.Item1 == OboTags.has_context))
                {
                    OboStanza currentContextAttStore = collection.Single(x => x.Id == attr.Item2);
                    VariableContextSpec context = null;
                    if (!contextCollection.TryGetValue(currentContextAttStore.Id, out context))
                    {
                        context = CreateContext(collection, currentXeoTermWithContext, attr.Item2, currentContextAttStore.Name);
                        CreateContextUnitSet(collection, context, attr.Item2);
                        contextCollection.Add(currentContextAttStore.Id, context);
                    }
                    currentXeoTermWithContext.term.ContextCollection.Add(currentContextAttStore.Name, context);
                    currentXeoTermWithContext.contextCollection.Add(context);
                }
            }

            /*
             * second run: create all contextes for Envirometsvariables with "has_enum" relations
             */
            foreach (OboStanza currentXeoTermWithContext in collection.Where(x => x.isEnvironmentVariableTerm && x.Any(y => y.Item1 == OboTags.has_enum)))
            {//iterate the attributelist for all used contexte
                foreach (Tuple<OboTags, string> attr in currentXeoTermWithContext.Where(x => x.Item1 == OboTags.has_enum))
                {
                    OboStanza currentContextAttStore = collection.Single(x => x.Id == attr.Item2);
                    VariableContextSpec context = null;
                    if (!contextCollection.TryGetValue(currentContextAttStore.Id, out context))
                    {
                        context = CreateContext(collection, currentXeoTermWithContext, attr.Item2, currentContextAttStore.Name);
                        CreateContextEnumeration(collection, context, attr.Item2);
                        contextCollection.Add(currentContextAttStore.Id, context);
                    }
                    currentXeoTermWithContext.term.ContextCollection.Add(currentContextAttStore.Name, context);
                    currentXeoTermWithContext.contextCollection.Add(context);
                }
            }
        }
    }
}
